/**
 * Copyright (C) 2008 by The Regents of the University of California
 * Redistribution of this file is permitted under the terms of the GNU
 * Public License (GPL).
 *
 * @author Junghoo "John" Cho <cho AT cs.ucla.edu>
 * @date 3/24/2008
 */

#include <cstdio>
#include <iostream>
#include <fstream>
#include "Bruinbase.h"
#include "SqlEngine.h"
#include "BTreeIndex.h"

using namespace std;

// external functions and variables for load file and sql command parsing 
extern FILE* sqlin;
int sqlparse(void);

RC SqlEngine::run(FILE* commandline)
{
  fprintf(stdout, "Bruinbase> ");

  // set the command line input and start parsing user input
  sqlin = commandline;
  sqlparse();  // sqlparse() is defined in SqlParser.tab.c generated from
               // SqlParser.y by bison (bison is GNU equivalent of yacc)

  return 0;
}

RC SqlEngine::select(int attr, const string& table, const vector<SelCond>& cond)
{
	RecordFile rf;   // RecordFile containing the table
	RecordId   rid;  // record cursor for table scanning

	RC     rc;
	int    key;     
	string value;
	int    count;
	int    diff;
  
	BTreeIndex bt;   // Possible B+ tree index
	bool index = false; // Use of index option

	bool key_flag = false;
	vector<int> key_eq;
	bool key_eq_single = true;
	vector<int> key_neq;
	vector<int> key_ge;
	bool key_ge_flag = false;
	int key_min = -2147483647;
	vector<int> key_le;
	bool key_le_flag = false;
	int key_max = 2147483647;
	bool value_flag = false;
	vector<string> value_eq;
	vector<string> value_neq;
	vector<string> value_le;
	vector<string> value_ge;
	vector<string> value_lt;
	vector<string> value_gt;
	
  
	/*** PROCESS CONDITIONS: For each condition.. *****************************/
	for(vector<SelCond>::size_type i = 0; i < cond.size(); i++) {
		
		switch(cond[i].attr) {
		case 1: // Conditioning on key
			key_flag = true;
			switch(cond[i].comp) {
			case SelCond::EQ: // EQUAL
				key_eq.push_back(atoi(cond[i].value));
				break;
			case SelCond::NE: // NOT EQUAL
				key_neq.push_back(atoi(cond[i].value));
			case SelCond::GT: // GREATER THAN
				key_ge.push_back(atoi(cond[i].value) + 1);
				key_ge_flag = true;
				break;
			case SelCond::LT: // LESS THAN
				key_le.push_back(atoi(cond[i].value) - 1);
				key_le_flag = true;
				break;
			case SelCond::GE: // GREATER THAN OR EQUAL TO
				key_ge.push_back(atoi(cond[i].value));
				key_ge_flag = true;
				break;
			case SelCond::LE: // LESS THAN OR EQUAL TO
				key_le.push_back(atoi(cond[i].value));
				key_le_flag = true;
				break;
			}
			
			break;
			
		case 2: // Conditioning on value
			value_flag = true;
			switch(cond[i].comp) {
			case SelCond::EQ: // EQUAL
				value_eq.push_back(cond[i].value);
				break;
			case SelCond::NE: // NOT EQUAL
				value_neq.push_back(cond[i].value);
				break;
			case SelCond::GE: // GREATER THAN OR EQUAL TO
				value_ge.push_back(cond[i].value);
				break;
			case SelCond::LE: // LESS THAN OR EQUAL TO
				value_le.push_back(cond[i].value);
				break;
			case SelCond::GT: // GREATER THAN
				value_gt.push_back(cond[i].value);
				break;
			case SelCond::LT: // LESS THAN
				value_lt.push_back(cond[i].value);
				break;
			}
		
			break;
		}
		

	} // END loop on conditions
  	#ifdef DEBUG_SELECT
	cerr << ">>key_eq size: " << key_eq.size() << endl;
	cerr << ">>key_neq size: " << key_neq.size() << endl;
	cerr << ">>key_ge size: " << key_ge.size() << endl;
	cerr << ">>key_le size: " << key_le.size() << endl;
	cerr << ">>value_eq size: " << value_eq.size() << endl;
	cerr << ">>value_neq size: " << value_neq.size() << endl;
	cerr << ">>value_ge size: " << value_ge.size() << endl;
	cerr << ">>value_gt size: " << value_gt.size() << endl;
	cerr << ">>value_le size: " << value_le.size() << endl;
	cerr << ">>value_lt size: " << value_lt.size() << endl;
	#endif
  
	// open the table file
	if ((rc = rf.open(table + ".tbl", 'r')) < 0) {
		fprintf(stderr, "Error: table %s does not exist\n", table.c_str());
		return rc;
	}
  
	// open index
	rc = bt.open(table + ".idx", 'r');
	if(rc!=0) index = false;
	else      index = true;
  
	#ifdef DEBUG_SELECT
	cerr << ">>index flag: " << index << endl << endl;
	#endif
  
	/*** SELECT WITHOUT INDEXING ***/
	if (index == false || key_flag == false) {
  
		// scan the table file from the beginning
		rid.pid = rid.sid = 0;
		count = 0;
		while (rid < rf.endRid()) {
			// read the tuple
			if ((rc = rf.read(rid, key, value)) < 0) {
			fprintf(stderr, "Error: while reading a tuple from table %s\n", table.c_str());
			goto exit_select;
			}

			// check the conditions on the tuple
			for (unsigned i = 0; i < cond.size(); i++) {
				// compute the difference between the tuple value and the condition value
				switch (cond[i].attr) {
				case 1:
					diff = key - atoi(cond[i].value);
					break;
				case 2:
					diff = strcmp(value.c_str(), cond[i].value);
					break;
				}

				// skip the tuple if any condition is not met
				switch (cond[i].comp) {
				case SelCond::EQ:
					if (diff != 0) goto next_tuple;
					break;
				case SelCond::NE:
					if (diff == 0) goto next_tuple;
					break;
				case SelCond::GT:
					if (diff <= 0) goto next_tuple;
					break;
				case SelCond::LT:
					if (diff >= 0) goto next_tuple;
					break;
				case SelCond::GE:
					if (diff < 0) goto next_tuple;
					break;
				case SelCond::LE:
					if (diff > 0) goto next_tuple;
					break;
				}
			}

			// the condition is met for the tuple. 
			// increase matching tuple counter
			count++;

			// print the tuple 
			switch (attr) {
			case 1:  // SELECT key
				fprintf(stdout, "%d\n", key);
				break;
			case 2:  // SELECT value
				fprintf(stdout, "%s\n", value.c_str());
				break;
			case 3:  // SELECT *
				fprintf(stdout, "%d '%s'\n", key, value.c_str());
				break;
			}

			// move to the next tuple
			next_tuple:
			++rid;
		}
  
	}
	/** THIS IS THE SELECT WITH INDEX!! **/
	else
	{
		IndexCursor cursor;
  
		#ifdef DEBUG_SELECT
		cerr << ">>Using index for select..." << endl;
		#endif
	
		/*** Analyze the conditions *********************************************/
		// Calculate minimum key
		if (key_ge_flag) {
			for(vector<int>::size_type i = 0; i < key_ge.size(); i++) {
				if (i == 0)
					key_min = key_ge[i];
				else {
					if (key_ge[i] > key_min)
						key_min = key_ge[i];
				}
			}
		}
		#ifdef DEBUG_SELECT
		cerr << ">>key_min: " << key_min << endl;
		#endif
	
		// Calculate maximum key
		if (key_le_flag) {
			for(vector<int>::size_type i = 0; i < key_le.size(); i++) {
				if (i == 0)
					key_max = key_le[i];
				else {
					if (key_le[i] < key_max)
						key_max = key_le[i];
				}
			}
		}
		#ifdef DEBUG_SELECT
		cerr << ">>key_max: " << key_max << endl;
		#endif
	
		// test validity of EQ
		for(vector<int>::size_type i = 0; i < key_eq.size(); i++) {
			for(vector<int>::size_type j = i; j < key_eq.size(); j++) {
				if (key_eq[i] != key_eq[j]) {
					key_eq_single = false;
					break;
				}
			}
		}
		#ifdef DEBUG_SELECT
		cerr << ">>key_eq_single: " << key_eq_single << endl;
		#endif
	
		/******* Make the appropriate lookups and print *****************************/
		// If there is a key EQ condition
		if (key_flag && key_eq.size() > 0) {
	
			#ifdef DEBUG_SELECT
			cerr << ">>Using Key EQ.." << endl;
			#endif
		
			if (key_eq_single == true && // If the key conditions match
				(key_ge_flag == false || key_eq[0] >= key_min) &&
				(key_le_flag == false || key_eq[0] <= key_max)) {
			
				#ifdef DEBUG_SELECT
				cerr << ">>key_eq: looking up..." << endl;
				#endif
			
				// look up in index
				rc = bt.locate(key_eq[0], cursor); if(rc!=0) return rc;
				#ifdef DEBUG_SELECT
				cerr << ">>key_eq: located key." << endl;
				#endif
				rc = bt.readForward(cursor, key, rid); if(rc!=0) return rc;
			
				#ifdef DEBUG_SELECT
				cerr << ">>key_eq: key = " << key << endl;
				#endif
			
				// test that the key does not exist in NEQ list
				for(vector<int>::size_type i = 0; i < key_neq.size(); i++) {
					if(key == key_neq[i]) {
						key_eq_single = false;
						break;
					}
				}
			
				// test that the key exists in table
				if(key != key_eq[0]) {
					key_eq_single = false;
				}
				// test validity with value conditions
				else if (value_flag) {
					rc = rf.read(rid, key, value); if(rc!=0)return rc;
					for(vector<string>::size_type i = 0; i < value_eq.size(); i++) {
						if(value != value_eq[i]) {
							key_eq_single = false;
							break;
						}
					}
					for(vector<string>::size_type i = 0; key_eq_single && i < value_neq.size(); i++) {
						if(value == value_neq[i]) {
							key_eq_single = false;
							break;
						}
					}
					for(vector<string>::size_type i = 0; key_eq_single && i < value_ge.size(); i++) {
						if(value < value_ge[i]) {
							key_eq_single = false;
							break;
						}
					}
					for(vector<string>::size_type i = 0; key_eq_single && i < value_gt.size(); i++) {
						if(value <= value_neq[i]) {
							key_eq_single = false;
							break;
						}
					}
					for(vector<string>::size_type i = 0; key_eq_single && i < value_le.size(); i++) {
						if(value > value_le[i]) {
							key_eq_single = false;
							break;
						}
					}
					for(vector<string>::size_type i = 0; key_eq_single && i < value_lt.size(); i++) {
						if(value >= value_lt[i]) {
							key_eq_single = false;
							break;
						}
					}
				}
				
				// If the key was valid
				if (key_eq_single) {
			
					#ifdef DEBUG_SELECT
					cerr << ">>key_eq valid, now printing" << endl;
					#endif
			
					// increase count
					count++;
			
					switch (attr) {
					case 1:  // SELECT key
						fprintf(stdout, "%d\n", key);
						break;
					case 2:  // SELECT value
						if (value_flag == false) {
							rc = rf.read(rid, key, value); if(rc!=0)return rc;
						}
						fprintf(stdout, "%s\n", value.c_str());
						break;
					case 3:  // SELECT *
						if (value_flag == false) {
							rc = rf.read(rid, key, value); if(rc!=0)return rc;
						}
						fprintf(stdout, "%d '%s'\n", key, value.c_str());
						break;
					}
				}
			}
		}
		// No EQ Condition
		else if(key_flag) {
			//int currentkey = key_min;
			if (key_min <= key_max) {
				rc = bt.locate(key_min, cursor); if(rc!=0)return rc;
				rc = bt.readForward(cursor, key, rid); if(rc!=0)return rc;
				//currentkey = key;
				#ifdef DEBUG_SELECT
				cerr << ">>First key: " << key << endl;
				#endif
				while(cursor.pid >= 0 && key <= key_max) {
					bool contflag = false;
					#ifdef DEBUG_SELECT
					cerr << ">>Current key: " << key << endl;
					#endif
					
					// check that key doesn't exist in NEQ list
					for(vector<int>::size_type i = 0; i < key_neq.size(); i++) {
						if(key == key_neq[i]) contflag = true;;
					}
				
					// check value conditions
					if (value_flag) {
						rc = rf.read(rid, key, value); if(rc!=0)return rc;
						for(vector<string>::size_type i = 0; i < value_eq.size(); i++) {
							if(value != value_eq[i]) contflag = true;
						}
						for(vector<string>::size_type i = 0; i < value_neq.size(); i++) {
							if(value == value_neq[i]) contflag = true;
						}
					}
				
					// If this tuple didn't match one of the conditions, move on
					if (contflag) {
						bt.readForward(cursor, key, rid);
						//currentkey = key;
						continue;
					}
				
					// increase count
					count++;
			
					switch (attr) {
					case 1:  // SELECT key
						fprintf(stdout, "%d\n", key);
						break;
					case 2:  // SELECT value
						if (value_flag == false) {
							rc = rf.read(rid, key, value); if(rc!=0)return rc;
						}
						fprintf(stdout, "%s\n", value.c_str());
						break;
					case 3:  // SELECT *
						if (value_flag == false) {
							rc = rf.read(rid, key, value); if(rc!=0)return rc;
						}
						fprintf(stdout, "%d '%s'\n", key, value.c_str());
						break;
					}
				
					bt.readForward(cursor, key, rid);
					//currentkey = key;
				}
				// Print the last key
				if(key <= key_max) {
					count++;
			
					switch (attr) {
					case 1:  // SELECT key
						fprintf(stdout, "%d\n", key);
						break;
					case 2:  // SELECT value
						if (value_flag == false) {
							rc = rf.read(rid, key, value); if(rc!=0)return rc;
						}
						fprintf(stdout, "%s\n", value.c_str());
						break;
					case 3:  // SELECT *
						if (value_flag == false) {
							rc = rf.read(rid, key, value); if(rc!=0)return rc;
						}
						fprintf(stdout, "%d '%s'\n", key, value.c_str());
						break;
					}
				}
			}
		}
	}
	
/*    // increase matching tuple counter
    count++;

    // print the tuple 
    switch (attr) {
    case 1:  // SELECT key
		fprintf(stdout, "%d\n", key);
		break;
    case 2:  // SELECT value
		fprintf(stdout, "%s\n", value.c_str());
		break;
    case 3:  // SELECT *
		fprintf(stdout, "%d '%s'\n", key, value.c_str());
		break;
    }
  }*/

	// print matching tuple count if "select count(*)"
	if (attr == 4) {
		fprintf(stdout, "%d\n", count);
	}
	rc = 0;

	// close the table file and return
	exit_select:
	rf.close();
	return rc;
}

RC SqlEngine::load(const string& table, const string& loadfile, bool index)
{
  /* your code here */
  RecordFile rf;   // RecordFile containing the table
  RecordId   rid;  // record cursor for table scanning
  
  BTreeIndex bt;

  RC     rc;
  string line;
  int    key;
  string value;
  int    count;
  int    diff;
  
  // open the loadfile
  ifstream myloadfile(loadfile.c_str());
  
  // open the table file
  if ((rc = rf.open(table + ".tbl", 'w')) != 0) {
    fprintf(stderr, "Error: table %s does not exist\n", table.c_str());
    return rc;
  }
  
  // If index is true, open index file
  if (index == true) {
	rc = bt.open(table + ".idx", 'w');
	if(rc!=0) {
		fprintf(stderr, "Error: could not open index file\n");
		return rc;
	}
  }
  
  // parse each line from the loadfile and append the tuple
  if (myloadfile.is_open()) {
    while (myloadfile.good() && getline(myloadfile,line)) {
	  SqlEngine::parseLoadLine(line, key, value); // parse the line into (key,value)
      rid = rf.endRid();                 // find which rid to write into
	  if ((rc = rf.append(key, value, rid)) != 0) { // append the tuple into the record
	    fprintf(stderr, "Error: could not append tuple\n");
		return rc;
	  }
	  if (index == true) {
		rc = bt.insert(key,rid);
		if(rc!=0) {
			fprintf(stderr, "Error: could not insert to tree\n");
			return rc;
		}
	  }
    }
  }
  
  // close the files
  rf.close();
  if (index==true) bt.close();
  myloadfile.close();
  
  return 0;
}

RC SqlEngine::parseLoadLine(const string& line, int& key, string& value)
{
    const char *s;
    char        c;
    string::size_type loc;
    
    // ignore beginning white spaces
    c = *(s = line.c_str());
    while (c == ' ' || c == '\t') { c = *++s; }

    // get the integer key value
    key = atoi(s);

    // look for comma
    s = strchr(s, ',');
    if (s == NULL) { return RC_INVALID_FILE_FORMAT; }

    // ignore white spaces
    do { c = *++s; } while (c == ' ' || c == '\t');
    
    // if there is nothing left, set the value to empty string
    if (c == 0) { 
        value.erase();
        return 0;
    }

    // is the value field delimited by ' or "?
    if (c == '\'' || c == '"') {
        s++;
    } else {
        c = '\n';
    }

    // get the value string
    value.assign(s);
    loc = value.find(c, 0);
    if (loc != string::npos) { value.erase(loc); }

    return 0;
}
